#include "fasterxml.h"

#define _DEBUG		0

#ifndef STRCMP
#define STRCMP(_a_,_C_,_b_) ( strcmp(_a_,_b_) _C_ 0 )
#define STRNCMP(_a_,_C_,_b_,_n_) ( strncmp(_a_,_b_,_n_) _C_ 0 )
#endif

#ifndef STRICMP
#if ( defined _WIN32 )
#define STRICMP(_a_,_C_,_b_) ( stricmp(_a_,_b_) _C_ 0 )
#define STRNICMP(_a_,_C_,_b_,_n_) ( strnicmp(_a_,_b_,_n_) _C_ 0 )
#elif ( defined __unix ) || ( defined __linux )
#define STRICMP(_a_,_C_,_b_) ( strcasecmp(_a_,_b_) _C_ 0 )
#define STRNICMP(_a_,_C_,_b_,_n_) ( strncasecmp(_a_,_b_,_n_) _C_ 0 )
#endif
#endif

#ifndef MAX
#define MAX(_a_,_b_) (_a_>_b_?_a_:_b_)
#endif

int __FASTERXML_VERSION_1_2_0 = 0 ;

#define ENCODING_GB18030	54936
#define ENCODING_UTF8		65001

#if ( defined _WIN32 )
__declspec( thread ) int __FASTERXML_RECCOUNT = 0 ;
__declspec( thread ) int __FASTERXML_ENCODING = ENCODING_UTF8 ;
#elif ( defined __linux__ ) || ( defined _AIX )
__thread  int __FASTERXML_RECCOUNT = 0 ;
__thread  int __FASTERXML_ENCODING = ENCODING_UTF8 ;
#endif

#define MAXCNT_SKIPTAG		32

char	*g_pcSkipXmlTags[ MAXCNT_SKIPTAG + 1 ] = { NULL } ;
int	g_nSkipXmlTagsLen[ MAXCNT_SKIPTAG + 1 ] = { 0 } ;
int	g_nSkipXmlTagCount = 0 ;

#define FASTERXML_INFO_END_OF_BUFFER		13

int AddSkipXmlTag( char *tag )
{
	if( g_nSkipXmlTagCount + 1 > MAXCNT_SKIPTAG )
		return FASTERXML_ERROR_TOO_MANY_SKIPTAGS;
	
	g_pcSkipXmlTags[g_nSkipXmlTagCount] = strdup( tag ) ;
	if( g_pcSkipXmlTags[g_nSkipXmlTagCount] == NULL )
		return FASTERXML_ERROR_ALLOC;
	g_nSkipXmlTagsLen[g_nSkipXmlTagCount] = strlen(g_pcSkipXmlTags[g_nSkipXmlTagCount]) ;
	
	g_nSkipXmlTagCount++;
	
	return 0;
}

int AddSkipHtmlTags()
{
	AddSkipXmlTag( "doctype" );
	
	AddSkipXmlTag( "br" );
	AddSkipXmlTag( "hr" );
	AddSkipXmlTag( "image" );
	AddSkipXmlTag( "img" );
	AddSkipXmlTag( "input" );
	AddSkipXmlTag( "link" );
	AddSkipXmlTag( "meta" );
	
	AddSkipXmlTag( "area" );
	AddSkipXmlTag( "base" );
	AddSkipXmlTag( "col" );
	AddSkipXmlTag( "command" );
	AddSkipXmlTag( "embed" );
	AddSkipXmlTag( "keygen" );
	AddSkipXmlTag( "param" );
	AddSkipXmlTag( "source" );
	AddSkipXmlTag( "track" );
	AddSkipXmlTag( "wbr" );
	
	return 0;
}

void CleanSkipXmlTags()
{
	for( g_nSkipXmlTagCount-- ; g_nSkipXmlTagCount >= 0 ; g_nSkipXmlTagCount-- )
	{
		if( g_pcSkipXmlTags[g_nSkipXmlTagCount] != NULL )
		{
			free( g_pcSkipXmlTags[g_nSkipXmlTagCount] );
			g_pcSkipXmlTags[g_nSkipXmlTagCount] = NULL ;
			g_nSkipXmlTagsLen[g_nSkipXmlTagCount] = 0 ;
		}
	}
	
	g_nSkipXmlTagCount = 0 ;
	
	return;
}

#define MAXCNT_LEAFTAG		32

char	*g_pcLeafXmlTags[ MAXCNT_LEAFTAG + 1 ] = { NULL } ;
int	g_nLeafXmlTagsLen[ MAXCNT_LEAFTAG + 1 ] = { 0 } ;
int	g_nLeafXmlTagCount = 0 ;

#define FASTERXML_INFO_END_OF_BUFFER		13

int AddLeafXmlTag( char *tag )
{
	if( g_nLeafXmlTagCount + 1 > MAXCNT_LEAFTAG )
		return FASTERXML_ERROR_TOO_MANY_LEAFTAGS;
	
	g_pcLeafXmlTags[g_nLeafXmlTagCount] = strdup( tag ) ;
	if( g_pcLeafXmlTags[g_nLeafXmlTagCount] == NULL )
		return FASTERXML_ERROR_ALLOC;
	g_nLeafXmlTagsLen[g_nLeafXmlTagCount] = strlen(g_pcLeafXmlTags[g_nLeafXmlTagCount]) ;
	
	g_nLeafXmlTagCount++;
	
	return 0;
}

int AddLeafHtmlTags()
{
	AddLeafXmlTag( "script" );
	
	return 0;
}

void CleanLeafXmlTags()
{
	for( g_nLeafXmlTagCount-- ; g_nLeafXmlTagCount >= 0 ; g_nLeafXmlTagCount-- )
	{
		if( g_pcLeafXmlTags[g_nLeafXmlTagCount] != NULL )
		{
			free( g_pcLeafXmlTags[g_nLeafXmlTagCount] );
			g_pcLeafXmlTags[g_nLeafXmlTagCount] = NULL ;
			g_nLeafXmlTagsLen[g_nLeafXmlTagCount] = 0 ;
		}
	}
	
	g_nLeafXmlTagCount = 0 ;
	
	return;
}

#define FASTERXML_TOKEN_EOF		0	
#define FASTERXML_TOKEN_LAB		1	/* < */
#define FASTERXML_TOKEN_LAB_SP		2	/* <? or <! */
#define FASTERXML_TOKEN_SLASH		3	/* / or ? */
#define FASTERXML_TOKEN_RAB		4	/* > */
#define FASTERXML_TOKEN_RHAB		5	/* ?> */
#define FASTERXML_TOKEN_TEXT		6
#define FASTERXML_TOKEN_TEXT_CDATA	61	/* <![CDATA[...]]> */
#define FASTERXML_TOKEN_PROPNAME	11
#define FASTERXML_TOKEN_PROPVALUE	12
#define FASTERXML_TOKEN_EQ		15

#define TOKENPROPERTY(_base_,_begin_,_len_,_eof_ret_)			\
	do								\
	{								\
		if( (_base_) == NULL )					\
		{							\
			return _eof_ret_;				\
		}							\
		while(1)						\
		{							\
			for( ; *(_base_) ; (_base_)++ )			\
			{						\
				if( *(_base_) != ' ' && *(_base_) != '\t' && *(_base_) != '\r' && *(_base_) != '\n' )	\
					break;				\
			}						\
			if( *(_base_) == '\0' )				\
			{						\
				return _eof_ret_;			\
			}						\
			else if( (_base_)[0] == '<' && (_base_)[1] == '!' && (_base_)[2] == '-' && (_base_)[3] == '-' )	\
			{												\
				for( (_base_)+=4 ; *(_base_) ; (_base_)++ )						\
				{											\
					if( (_base_)[0] == '-' && (_base_)[1] == '-' && (_base_)[2] == '>' )		\
						break;									\
				}											\
				if( *(_base_) == '\0' )									\
				{											\
					return _eof_ret_;								\
				}											\
				(_base_)+=3;										\
				continue;										\
			}												\
			break;						\
		}							\
		(_begin_) = (_base_) ;					\
		if(	( (_base_)[0] == '>' )				\
			|| ( (_base_)[0] == '/' && (_base_)[1] == '>' )	\
			|| ( (_base_)[0] == '?' && (_base_)[1] == '>' ) )	\
		{							\
			return _eof_ret_;				\
		}							\
		else if( (_base_)[0] == '"' || (_base_)[0] == '\'' )	\
		{							\
			char	mark = (_base_)[0] ;			\
			(_base_)++;					\
			(_begin_) = (_base_) ;				\
			for( ; *(_base_) ; (_base_)++ )			\
			{						\
				if( (unsigned char)*(_base_) > 127 )		\
				{						\
					if( __FASTERXML_ENCODING == ENCODING_UTF8 )	\
					{						\
						if( (unsigned char)*(_base_) >> 5 == 0x06 )	\
							(_base_)++;			\
						else if( (unsigned char)*(_base_) >> 4 == 0x0E )	\
							(_base_)+=2;			\
						else if( (unsigned char)*(_base_) >> 3 == 0x1E )	\
							(_base_)+=3;			\
						else					\
							(_base_)++;			\
					}					\
					else if( __FASTERXML_ENCODING == ENCODING_GB18030 )	\
					{					\
						(_base_)++;			\
					}					\
					continue;				\
				}						\
				if( *(_base_) == mark )			\
					break;				\
			}						\
			(_len_) = (_base_) - (_begin_) ;		\
			(_base_)++;					\
			break;						\
		}							\
		else if( (_base_)[0] == '=' )				\
		{							\
			(_begin_) = (_base_) ;				\
			(_len_) = 1 ;					\
			(_base_)++;					\
			break;						\
		}							\
		else							\
		{							\
			(_begin_) = (_base_) ;				\
			for( ; *(_base_) ; (_base_)++ )			\
			{						\
				if( *(_base_) == ' ' || *(_base_) == '\t' || *(_base_) == '\r' || *(_base_) == '\n' || *(_base_) == '=' || *(_base_) == '?' || *(_base_) == '>' )	\
					break;				\
			}						\
			(_len_) = (_base_) - (_begin_) ;		\
			break;						\
		}							\
	}								\
	while(0);							\

int TravelXmlPropertiesBuffer( char *properties , int properties_len , int type , char *xpath , int xpath_len , int xpath_size , char *content , int content_len , funcCallbackOnXmlProperty *pfuncCallbackOnXmlProperty , void *p )
{
	char		*begin = NULL ;
	int		len ;
	
	char		*propname = NULL ;
	int		propname_len ;
	char		*propvalue = NULL ;
	int		propvalue_len ;
	
	int		xpath_newlen = 0 ;
	
	int		nret = 0 ;
	
	while(1)
	{
		TOKENPROPERTY( properties , begin , len , 0 )
		propname = begin ;
		propname_len = len ;
		
		if( properties[0] == ' ' || properties[0] == '>' )
		{
			propvalue = "" ;
			propvalue_len = 0 ;
		}
		else
		{
			TOKENPROPERTY( properties , begin , len , FASTERXML_ERROR_END_OF_BUFFER )
			if( ! ( begin[0] == '=' && len == 1 ) )
				return FASTERXML_ERROR_XML_INVALID-FASTERXML_TOKEN_EQ;
			
			TOKENPROPERTY( properties , begin , len , FASTERXML_ERROR_END_OF_BUFFER )
			propvalue = begin ;
			propvalue_len = len ;
		}
		
		if( pfuncCallbackOnXmlProperty )
		{
			if( xpath )
			{
				if( xpath_len + 1 + propname_len < xpath_size-1 - 1 )
				{
					sprintf( xpath + xpath_len , ".%.*s" , (int)propname_len , propname );
					xpath_newlen = xpath_len + 1 + propname_len ;
				}
				else if( xpath_len + 1 + 1 <= xpath_size-1 )
				{
					sprintf( xpath + xpath_len , ".*" );
					xpath_newlen = xpath_len + 1 + 1 ;
				}
				else
				{
					xpath_newlen = xpath_len ;
				}
			}
			
#define __XML_ENCODING		"/xml.encoding"
#define __XML_ENCODING_UTF8	"UTF-8"
#define __XML_ENCODING_GB18030	"GB"
			if( xpath_newlen == sizeof(__XML_ENCODING)-1 && strncasecmp( xpath , __XML_ENCODING , sizeof(__XML_ENCODING)-1 ) == 0 )
			{
				if( propvalue_len == sizeof(__XML_ENCODING_UTF8)-1 && strncasecmp( propvalue , __XML_ENCODING_UTF8 , sizeof(__XML_ENCODING_UTF8)-1 ) == 0 )
					__FASTERXML_ENCODING = ENCODING_UTF8 ;
				else if( propvalue_len >= 3 || strncasecmp( propvalue , __XML_ENCODING_GB18030 , sizeof(__XML_ENCODING_GB18030)-1 ) )
					__FASTERXML_ENCODING = ENCODING_GB18030 ;
			}
			
			nret = (*pfuncCallbackOnXmlProperty)( type , xpath , xpath_newlen , xpath_size , propname , propname_len , propvalue , propvalue_len , content , content_len , p ) ;
			if( nret == FASTERXML_INFO_INTERRUPT_TRAVEL )
				return FASTERXML_INFO_INTERRUPT_TRAVEL;
			else if( nret > 0 )
				break;
			else if( nret < 0 )
				return nret;
		}
	}
	
	return 0;
}

#define TOKENCONTENT(_base_,_begin_,_len_,_type_,_leaf_n_)		\
	do								\
	{								\
		int	CDATA_flag = 0 ;				\
		int	REMARK_flag = 0 ;				\
		(_begin_) = (_base_) ;					\
		for( ; *(_base_) ; (_base_)++ )				\
		{							\
			if( (unsigned char)*(_base_) > 127 )		\
			{						\
				if( __FASTERXML_ENCODING == ENCODING_UTF8 )	\
				{						\
					if( (unsigned char)*(_base_) >> 5 == 0x06 )	\
						(_base_)++;			\
					else if( (unsigned char)*(_base_) >> 4 == 0x0E )	\
						(_base_)+=2;			\
					else if( (unsigned char)*(_base_) >> 3 == 0x1E )	\
						(_base_)+=3;			\
					else					\
						(_base_)++;			\
				}					\
				else if( __FASTERXML_ENCODING == ENCODING_GB18030 )	\
				{					\
					(_base_)++;			\
				}					\
				continue;				\
			}						\
			if( CDATA_flag == 0 && REMARK_flag == 0 && (_base_)[0] == '<' )	\
			{						\
				if( (_base_)[1] == '!' && (_base_)[2] == '-' && (_base_)[3] == '-' )	\
				{					\
					REMARK_flag = 1 ;		\
				}					\
				else if( (_base_)[1] == '!' && (_base_)[2] == '[' && (_base_)[3] == 'C' && (_base_)[4] == 'D' && (_base_)[5] == 'A' && (_base_)[6] == 'T' && (_base_)[7] == 'A' && (_base_)[8] == '[' )\
				{					\
					(_begin_) = (_base_) + 9 ;	\
					CDATA_flag = 1 ;		\
				}					\
				else					\
				{					\
					if( _leaf_n_ >= 0 )	\
					{				\
						if( (_base_)[1] == '/' && STRNICMP( (_base_)+2 , == , g_pcLeafXmlTags[_leaf_n_] , g_nLeafXmlTagsLen[_leaf_n_] ) )	\
						{			\
							(_len_) = (int)((_base_)-(_begin_)) ;	\
							(_type_) = FASTERXML_TOKEN_TEXT ;	\
							break;		\
						}			\
					}				\
					else				\
					{				\
						(_len_) = (int)((_base_)-(_begin_)) ;	\
						(_type_) = FASTERXML_TOKEN_TEXT ;	\
						break;			\
					}				\
				}					\
			}						\
			else if( CDATA_flag == 1 && *(_base_) == ']' )	\
			{						\
				if( (_base_)[1] == ']' && (_base_)[2] == '>' )	\
				{					\
					(_len_) = (int)((_base_)-(_begin_)) ;	\
					(_type_) = FASTERXML_TOKEN_TEXT_CDATA ;	\
					(_base_) += 3 ;			\
					break;				\
				}					\
			}						\
			else if( REMARK_flag == 1 && (_base_)[1] == '-' && (_base_)[2] == '-' && (_base_)[3] == '>' )	\
			{						\
				(_base_) += 3 ;				\
				REMARK_flag = 0 ;			\
			}						\
		}							\
		if( *(_base_) == '\0' )					\
		{							\
			return FASTERXML_ERROR_END_OF_BUFFER;		\
		}							\
	}								\
	while(0);							\

#define TOKENXML(_base_,_begin_,_len_,_type_,_eof_ret_)			\
	do								\
	{								\
		if( (_base_) == NULL )					\
		{							\
			return _eof_ret_;				\
		}							\
		(_type_) = 0 ;						\
		while(1)						\
		{							\
			for( ; *(_base_) ; (_base_)++ )			\
			{						\
				if( *(_base_) != ' ' && *(_base_) != '\t' && *(_base_) != '\r' && *(_base_) != '\n' )	\
					break;				\
			}						\
			if( *(_base_) == '\0' )				\
			{						\
				return _eof_ret_;			\
			}						\
			if( (_base_)[0] == '<' && (_base_)[1] == '!' && (_base_)[2] == '-' && (_base_)[3] == '-' )	\
			{						\
				for( (_base_)+=4 ; *(_base_) ; (_base_)++ )	\
				{					\
					if( (_base_)[0] == '-' && (_base_)[1] == '-' && (_base_)[2] == '>' )	\
						break;			\
				}					\
				if( *(_base_) == '\0' )			\
				{					\
					return _eof_ret_;		\
				}					\
				(_base_)+=3;				\
				continue;				\
			}						\
			break;						\
		}							\
		if( (_type_) )						\
			break;						\
		if( (_base_)[0] == '<' )				\
		{							\
			if( (_base_)[1] == '?' )			\
			{						\
				(_begin_) = (_base_) ;			\
				(_len_) = 2 ;				\
				(_base_)+=2;				\
				(_type_) = FASTERXML_TOKEN_LAB_SP ;	\
				break;					\
			}						\
			else if( (_base_)[1] == '!' )			\
			{						\
				(_begin_) = (_base_) ;			\
				(_len_) = 2 ;				\
				(_base_)+=2;				\
				(_type_) = FASTERXML_TOKEN_LAB_SP ;	\
				break;					\
			}						\
			else						\
			{						\
				if( isprint((_base_)[1]) )		\
				{					\
					(_begin_) = (_base_) ;		\
					(_len_) = 1 ;			\
					(_base_)++;			\
					(_type_) = FASTERXML_TOKEN_LAB ;	\
					break;				\
				}					\
			}						\
		}							\
		if( ( (_base_)[0] == '/' ) )				\
		{							\
			(_begin_) = (_base_) ;				\
			(_len_) = 1 ;					\
			(_base_)++;					\
			(_type_) = FASTERXML_TOKEN_SLASH ;		\
			break;						\
		}							\
		if( (_base_)[0] == '>' )				\
		{							\
			(_begin_) = (_base_) ;				\
			(_len_) = 1 ;					\
			(_base_)++;					\
			(_type_) = FASTERXML_TOKEN_RAB ;			\
			break;						\
		}							\
		(_begin_) = (_base_) ;					\
		for( ++(_base_) ; *(_base_) ; (_base_)++ )		\
		{							\
			if( (unsigned char)*(_base_) > 127 )		\
				continue;				\
			if( *(_base_) == ' ' || *(_base_) == '\t' || *(_base_) == '\r' || *(_base_) == '\n' || *(_base_) == '<' || *(_base_) == '/' || *(_base_) == '?' || *(_base_) == '>' )	\
			{						\
				if( *(_base_) == '>' && (unsigned char)(_base_)[-1] > 127 )	\
					continue;			\
				(_len_) = (_base_) - (_begin_) ;	\
				(_type_) = FASTERXML_TOKEN_TEXT ;	\
				break;					\
			}						\
		}							\
		if( *(_base_) == '\0' )					\
		{							\
			return _eof_ret_;				\
		}							\
	}								\
	while(0);							\

static int _TravelXmlBuffer( register char **xml_ptr , char *xpath , int xpath_len , int xpath_size
	, funcCallbackOnXmlNode *pfuncCallbackOnXmlNode
	, funcCallbackOnXmlNode *pfuncCallbackOnEnterXmlNode
	, funcCallbackOnXmlNode *pfuncCallbackOnLeaveXmlNode
	, funcCallbackOnXmlNode *pfuncCallbackOnXmlLeaf
	, void *p , char *preread_node , char *preread_tag , int preread_tag_len )
{
	char		*begin = NULL ;
	int		len ;
	signed char	type = 0 , type2 = 0 ;
	
	int		close_flag ;
	int		i ;
	int		leaf_n ;
	
	char		*node = NULL ;
	int		node_len ;
	char		*tag = NULL ;
	int		tag_len ;
	char		*properties ;
	int		properties_len ;
	char		*content = NULL ;
	int		content_len ;
	int		xpath_newlen = 0 ;
	int		nret = 0 ;
	
	while(1)
	{
		type2++;
		close_flag = 0 ;
		
		if( preread_tag )
		{
			node = preread_node ;
			tag = preread_tag ;
			tag_len = preread_tag_len ;
			preread_tag = NULL ;
			goto _PREREAD_GO;
		}
		
		while(1)
		{
			TOKENXML(*xml_ptr,begin,len,type,FASTERXML_INFO_END_OF_BUFFER)
#if _DEBUG
printf( "%03dTOKENXML - '<' or '<!' or '<?' - type[%d] begin[%.*s]\n" , __LINE__ , type , len , begin );
#endif

			__FASTERXML_RECCOUNT++;
			if(( __FASTERXML_RECCOUNT == 1 ) && (type != FASTERXML_TOKEN_LAB) && (type != FASTERXML_TOKEN_LAB_SP))
				return FASTERXML_ERROR_XML_INVALID;

			else if( type == FASTERXML_TOKEN_LAB || type == FASTERXML_TOKEN_LAB_SP )
				break;

			/*if( type == FASTERXML_TOKEN_LAB || type == FASTERXML_TOKEN_LAB_SP )
				break;*/
		}
		if( type == FASTERXML_TOKEN_LAB_SP )
			close_flag = 1 ;
		
		node = begin ;
		
		TOKENXML(*xml_ptr,begin,len,type,FASTERXML_ERROR_END_OF_BUFFER)
#if _DEBUG
printf( "%03dTOKENXML - tag - type[%d] begin[%.*s]\n" , __LINE__ , type , len , begin );
#endif
		if( type == FASTERXML_TOKEN_SLASH )
		{
			char	*p = (*xml_ptr) ;
			TOKENXML(p,begin,len,type,FASTERXML_ERROR_END_OF_BUFFER)
			if( type != FASTERXML_TOKEN_TEXT )
				return FASTERXML_ERROR_XML_INVALID-FASTERXML_TOKEN_TEXT;
			
			for( i = 0 ; i < g_nSkipXmlTagCount ; i++ )
			{
				if( len == g_nSkipXmlTagsLen[i] && STRNICMP( begin , == , g_pcSkipXmlTags[i] , len ) )
					break;
			}
			if( i < g_nSkipXmlTagCount )
				continue;
			
			break;
		}
		
		tag = begin ;
		tag_len = len ;
		
_PREREAD_GO :
		for( leaf_n = 0 ; leaf_n < g_nLeafXmlTagCount ; leaf_n++ )
		{
			if( tag_len == g_nLeafXmlTagsLen[leaf_n] && STRNICMP( tag , == , g_pcLeafXmlTags[leaf_n] , tag_len ) )
				break;
		}
		if( leaf_n >= g_nLeafXmlTagCount )
			leaf_n = -1 ;
		
		properties = (*xml_ptr) ;
		
		for( ; *(*xml_ptr) ; (*xml_ptr)++ )
		{
			if( strchr( "\"'" , *(*xml_ptr) ) )
			{
				begin = (*xml_ptr) ;
				for( (*xml_ptr)++ ; *(*xml_ptr) ; (*xml_ptr)++ )
				{
					if( *(*xml_ptr) == (*begin) )
						break;
				}
				if( *(*xml_ptr) == '\0' )
					return FASTERXML_ERROR_END_OF_BUFFER;
			}
			else if( *(*xml_ptr) == '/' && *((*xml_ptr)+1) == '>' ) /* <.../> is valid */
			{
				close_flag = 1 ;
				(*xml_ptr)+=2;
				break;
			}
			else if( *(*xml_ptr) == '>' )
			{
				(*xml_ptr)++;
				break;
			}
		}
		if( *(*xml_ptr) == '\0' )
			return FASTERXML_ERROR_END_OF_BUFFER;
		
		properties_len = (*xml_ptr) - properties ;
#if _DEBUG
printf( "%03dTOKENXML - properties[%.*s]\n" , __LINE__ , properties_len , properties );
#endif
		
		for( i = 0 ; i < g_nSkipXmlTagCount ; i++ )
		{
			if( tag_len == g_nSkipXmlTagsLen[i] && STRNICMP( tag , == , g_pcSkipXmlTags[i] , tag_len ) )
				break;
		}
		if( i < g_nSkipXmlTagCount )
		{
#if _DEBUG
printf( "%03dTOKENXML - continue xml_ptr[%c]\n" , __LINE__ , *(*xml_ptr) );
#endif
			continue;
		}
		
		if( xpath )
		{
			if( xpath_len + 1 + tag_len < xpath_size-1 - 1 )
			{
				sprintf( xpath + xpath_len , "/%.*s" , (int)tag_len , tag );
				xpath_newlen = xpath_len + 1 + tag_len ;
			}
			else if( xpath_len + 1 + 1 <= xpath_size-1 )
			{
				sprintf( xpath + xpath_len , "/*" );
				xpath_newlen = xpath_len + 1 + 1 ;
			}
			else
			{
				xpath_newlen = xpath_len ;
			}
		}
		
		if( close_flag )
		{
			node_len = (*xml_ptr) - node ;
			
			if( pfuncCallbackOnXmlNode )
			{
				nret = (*pfuncCallbackOnXmlNode)( FASTERXML_NODE_BRANCH , xpath , xpath_newlen , xpath_size , node , node_len , tag , tag_len , properties , properties_len , NULL , 0 , p ) ;
				if( nret == FASTERXML_INFO_INTERRUPT_TRAVEL )
					return FASTERXML_INFO_INTERRUPT_TRAVEL;
				else if( nret > 0 )
					break;
				else if( nret < 0 )
					return nret;
			}
			continue;
		}
		
		TOKENCONTENT(*xml_ptr,begin,len,type2,leaf_n)
#if _DEBUG
printf( "%03dTOKENCONTENT - content - type[%d] begin[%.*s]\n" , __LINE__ , type , len , begin );
#endif
		content = begin ;
		content_len = len ;
		
		preread_node = begin ;
		
		TOKENXML(*xml_ptr,begin,len,type,FASTERXML_ERROR_END_OF_BUFFER)
#if _DEBUG
printf( "%03dTOKENXML - '<' - type[%d] begin[%.*s]\n" , __LINE__ , type , len , begin );
#endif
		if( type != FASTERXML_TOKEN_LAB )
			return FASTERXML_ERROR_XML_INVALID-FASTERXML_TOKEN_LAB;
		
		TOKENXML(*xml_ptr,begin,len,type,FASTERXML_ERROR_END_OF_BUFFER)
#if _DEBUG
printf( "%03dTOKENXML - '/' or '?' or other - type[%d] begin[%.*s]\n" , __LINE__ , type , len , begin );
#endif
		if( type == FASTERXML_TOKEN_SLASH )
		{
			TOKENXML(*xml_ptr,begin,len,type,FASTERXML_ERROR_END_OF_BUFFER)
#if _DEBUG
printf( "%03dTOKENXML - close[%.*s] - type[%d] begin[%.*s]\n" , __LINE__ , tag_len , tag , type , len , begin );
#endif
			if( STRNICMP( begin , != , tag , MAX(len,tag_len) ) )
				return FASTERXML_ERROR_XML_INVALID-FASTERXML_TOKEN_TEXT;
			
			TOKENXML(*xml_ptr,begin,len,type,FASTERXML_ERROR_END_OF_BUFFER)
#if _DEBUG
printf( "%03dTOKENXML - not '>' - type[%d] begin[%.*s]\n" , __LINE__ , type , len , begin );
#endif
			if( type != FASTERXML_TOKEN_RAB )
				return FASTERXML_ERROR_XML_INVALID-FASTERXML_TOKEN_RAB;
			
			node_len = (*xml_ptr) - node ;
			
			if( pfuncCallbackOnXmlLeaf )
			{
				nret = (*pfuncCallbackOnXmlLeaf)( (type2!=FASTERXML_TOKEN_TEXT_CDATA?FASTERXML_NODE_LEAF:FASTERXML_NODE_LEAF_CDATA) , xpath , xpath_newlen , xpath_size , node , node_len , tag , tag_len , properties , properties_len , content , content_len , p ) ;
				if( nret == FASTERXML_INFO_INTERRUPT_TRAVEL )
					return FASTERXML_INFO_INTERRUPT_TRAVEL;
				else if( nret > 0 )
					break;
				else if( nret < 0 )
					return nret;
			}
		}
		else
		{
			if( pfuncCallbackOnEnterXmlNode )
			{
				nret = (*pfuncCallbackOnEnterXmlNode)( FASTERXML_NODE_ENTER | FASTERXML_NODE_BRANCH , xpath , xpath_newlen , xpath_size , NULL , 0 , tag , tag_len , properties , properties_len , content , content_len , p ) ;
				if( nret == FASTERXML_INFO_INTERRUPT_TRAVEL )
					return nret;
				else if( nret > 0 )
					break;
				else if( nret < 0 )
					return nret;
			}
			
			nret = _TravelXmlBuffer( xml_ptr , xpath , xpath_newlen , xpath_size
						, pfuncCallbackOnXmlNode
						, pfuncCallbackOnEnterXmlNode
						, pfuncCallbackOnLeaveXmlNode
						, pfuncCallbackOnXmlLeaf
						, p , preread_node , begin , len ) ;
			if( nret == FASTERXML_INFO_INTERRUPT_TRAVEL )
				return FASTERXML_INFO_INTERRUPT_TRAVEL;
			else if( nret )
				return nret;
			
			TOKENXML(*xml_ptr,begin,len,type,FASTERXML_ERROR_END_OF_BUFFER)
#if _DEBUG
printf( "%03dTOKENXML - close[%.*s] - type[%d] begin[%.*s]\n" , __LINE__ , tag_len , tag , type , len , begin );
#endif
			if( STRNICMP( begin , != , tag , MAX(len,tag_len) ) )
				return FASTERXML_ERROR_XML_INVALID-FASTERXML_TOKEN_TEXT;
			
			if( xpath )
			{
				if( xpath_len + 1 + tag_len < xpath_size-1 - 1 )
				{
					sprintf( xpath + xpath_len , "/%.*s" , (int)tag_len , tag );
					xpath_newlen = xpath_len + 1 + tag_len ;
				}
				else if( xpath_len + 1 + 1 <= xpath_size-1 )
				{
					sprintf( xpath + xpath_len , "/*" );
					xpath_newlen = xpath_len + 1 + 1 ;
				}
				else
				{
					xpath_newlen = xpath_len ;
				}
			}
			
			content_len = begin - content - 2 ;
			
			TOKENXML(*xml_ptr,begin,len,type,FASTERXML_ERROR_END_OF_BUFFER)
#if _DEBUG
printf( "%03dTOKENXML - '>' - type[%d] begin[%.*s]\n" , __LINE__ , type , len , begin );
#endif
			if( type != FASTERXML_TOKEN_RAB )
				return FASTERXML_ERROR_XML_INVALID-FASTERXML_TOKEN_RAB;
			
			node_len = (*xml_ptr) - node ;
			
			if( pfuncCallbackOnLeaveXmlNode )
			{
				nret = (*pfuncCallbackOnLeaveXmlNode)( FASTERXML_NODE_LEAVE | FASTERXML_NODE_BRANCH , xpath , xpath_newlen , xpath_size , node , node_len , tag , tag_len , properties , properties_len , content , content_len , p ) ;
				if( nret == FASTERXML_INFO_INTERRUPT_TRAVEL )
					return FASTERXML_INFO_INTERRUPT_TRAVEL;
				else if( nret > 0 )
					break;
				else if( nret < 0 )
					return nret;
			}
		}
	}
	
	return 0;
}

int TravelXmlBuffer( char *xml_buffer , char *xpath , int xpath_size
		    , funcCallbackOnXmlNode *pfuncCallbackOnXmlNode
		    , void *p )
{
	char		*xml_ptr = xml_buffer ;
	
	int		nret = 0 ;
	
	__FASTERXML_RECCOUNT = 0 ;
	nret = _TravelXmlBuffer( & xml_ptr , xpath , 0 , xpath_size
				, pfuncCallbackOnXmlNode
				, pfuncCallbackOnXmlNode
				, pfuncCallbackOnXmlNode
				, pfuncCallbackOnXmlNode
				, p , NULL , NULL , 0 ) ;
	if( nret == FASTERXML_INFO_END_OF_BUFFER || nret == FASTERXML_INFO_INTERRUPT_TRAVEL || nret == 0 )
		return 0;
	else  
		return nret;
	

}

int TravelXmlBuffer4( char *xml_buffer , char *xpath , int xpath_size
		    , funcCallbackOnXmlNode *pfuncCallbackOnXmlNode
		    , funcCallbackOnXmlNode *pfuncCallbackOnEnterXmlNode
		    , funcCallbackOnXmlNode *pfuncCallbackOnLeaveXmlNode
		    , funcCallbackOnXmlNode *pfuncCallbackOnXmlLeaf
		    , void *p )
{
	char		*xml_ptr = xml_buffer ;
	
	int		nret = 0 ;
	

	__FASTERXML_RECCOUNT = 0 ;
	nret = _TravelXmlBuffer( & xml_ptr , xpath , 0 , xpath_size
				, pfuncCallbackOnXmlNode
				, pfuncCallbackOnEnterXmlNode
				, pfuncCallbackOnLeaveXmlNode
				, pfuncCallbackOnXmlLeaf
				, p , NULL , NULL , 0 ) ;
	if( nret == FASTERXML_INFO_END_OF_BUFFER || nret == FASTERXML_INFO_INTERRUPT_TRAVEL || nret == 0 )
		return 0;
	else  
		return nret;

}

